////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2009 Daniel F. Hanson
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
////////////////////////////////////////////////////////////////////////////////

using System.Collections.Generic;

namespace SilverLua.Compiler
{
   internal class FunctionStatementNode : StatementNode
   {
      private Symbol _firstName;
      private string[] _nameList;
      private int _paramCount;
      private StatementNode[] _block;
      private int _chunkIndex;
      
      internal FunctionStatementNode(Symbol firstName,
                                     string[] nameList,
                                     int paramCount,
                                     StatementNode[] block,
                                     int chunkIndex)
      {
         _firstName = firstName;
         _nameList = nameList;
         _paramCount = paramCount;
         _block = block;
         _chunkIndex = chunkIndex;
      }

      internal override void GenerateInstructions(Parser parser, int currentChunk)
      {
         // Generate instructions for the function in a new chunk.
         foreach (StatementNode stmt in _block)
         {
            stmt.GenerateInstructions(parser, _chunkIndex);
         }
         
         // Determine where and how to store our new function.
         if (_nameList.Length == 0)
         {
            if (_firstName.IsLocal)
            {
               // Local variable with no subscripting, so assign directly.
               EvaluateNode var = _firstName.Resolve();
               
               parser.ChunkList[currentChunk].Add(new Instruction(Opcode.Function,
                                                                  new[] { var.OperandType,
                                                                          OperandType.IntegerLiteral,
                                                                          OperandType.IntegerLiteral },
                                                                  new[] { var.OperandValue,
                                                                          _chunkIndex,
                                                                          _paramCount }));
            }
            else
            {
               // Global variable with no subscripting, so use SetGlobal.
               int destRegister = parser.ChunkList[currentChunk].AllocateTempRegister();
               
               parser.ChunkList[currentChunk].Add(new Instruction(Opcode.Function,
                                                                  new[] { OperandType.Value,
                                                                          OperandType.IntegerLiteral,
                                                                          OperandType.IntegerLiteral },
                                                                  new[] { destRegister,
                                                                          _chunkIndex,
                                                                          _paramCount }));
               parser.ChunkList[currentChunk].Add(new Instruction(Opcode.SetGlobal,
                                                                  new[] { OperandType.StringLiteral,
                                                                          OperandType.Value },
                                                                  new[] { parser.ChunkList[currentChunk].ResolveString(_firstName.Name),
                                                                          destRegister }));
            }
         }
         else
         {
            // Generate function in temporary register
            int functionTemp = parser.ChunkList[currentChunk].AllocateTempRegister();

            parser.ChunkList[currentChunk].Add(new Instruction(Opcode.Function,
                                                               new[] { OperandType.Value,
                                                                       OperandType.IntegerLiteral,
                                                                       OperandType.IntegerLiteral },
                                                               new[] { functionTemp,
                                                                       _chunkIndex,
                                                                       _paramCount }));
            
            // Subscripting necessary, so acquire table to subscript from.
            EvaluateNode table;
            
            if (_firstName.IsLocal)
            {
               // Local variable; get index directly.
               table = _firstName.Resolve();
            }
            else
            {
               // Global variable; use GetGlobal.
               table = new EvaluateNode { OperandType = OperandType.Value,
                                          OperandValue = parser.ChunkList[currentChunk].AllocateTempRegister() };
               
               parser.ChunkList[currentChunk].Add(new Instruction(Opcode.GetGlobal,
                                                                  new[] { table.OperandType,
                                                                          OperandType.StringLiteral },
                                                                  new[] { table.OperandValue,
                                                                          parser.ChunkList[currentChunk].ResolveString(_firstName.Name) }));
            }
            
            // Evaluate all but the last subscript.
            for (int i = 0; i < _nameList.Length - 1; ++i)
            {
               parser.ChunkList[currentChunk].Add(new Instruction(Opcode.GetTable,
                                                                  new[] { table.OperandType,
                                                                          table.OperandType,
                                                                          OperandType.StringLiteral },
                                                                  new[] { table.OperandValue,
                                                                          table.OperandValue,
                                                                          parser.ChunkList[currentChunk].ResolveString(_nameList[i]) }));
            }
            
            // Use SetTable for the last subscript.
            parser.ChunkList[currentChunk].Add(new Instruction(Opcode.SetTable,
                                                               new[] { table.OperandType,
                                                                       OperandType.StringLiteral,
                                                                       OperandType.Value },
                                                               new[] { table.OperandValue,
                                                                       parser.ChunkList[currentChunk].ResolveString(_nameList[_nameList.Length-1]),
                                                                       functionTemp }));
         }
      }
   }
}
